// src/main/services/shell-service.ts
import { exec, ExecException, ChildProcess } from 'child_process';
import { ipcMain } from 'electron';
import log from 'electron-log';
import { EventEmitter } from 'events';

// ==================== 类型定义 ====================
export interface ShellResult {
  stdout: string;
  stderr: string;
  code: number | null;
}

export interface ShellExecOptions {
  timeout?: number; // 毫秒，默认 10 秒
  cwd?: string;
  env?: NodeJS.ProcessEnv;
  signal?: AbortSignal; // 支持取消操作
}

// 流式执行选项
export interface ShellExecStreamOptions extends ShellExecOptions {
  onStdout?: (data: string) => void;
  onStderr?: (data: string) => void;
}

export interface BatchExecOptions {
  stopOnError?: boolean;
  parallel?: boolean; // 是否并行执行
}

// ==================== 服务实现 ====================
export class ShellService extends EventEmitter {
  private activeProcesses = new Set<ChildProcess>();

  /**
   * 安全执行命令（带自动清理）
   */
  public async exec(
    command: string,
    options: ShellExecOptions = {}
  ): Promise<ShellResult> {
    this.validateCommand(command);

    const { timeout = 10000, cwd, env, signal } = options;

    log.info("执行命令：", command);

    return new Promise((resolve, reject) => {
      // 支持 AbortController 取消
      if (signal?.aborted) {
        return reject(new Error('Command was aborted'));
      }

      const child = exec(
        command,
        {
          encoding: 'buffer', // 原始二进制输出
          cwd: cwd || "D:/electron-vite-box/out",
          env: { ...process.env, ...env },
          timeout: timeout - 100 // 比外层超时略短
        },
        (error, stdout, stderr) => {
          const decoder = new TextDecoder('utf-8'); // 根据实际编码调整
          this.cleanupProcess(child);
          clearTimeout(timer);
          if (error) {
            resolve({
              stdout: decoder.decode(stdout).trim(),
              stderr: decoder.decode(stderr).trim() || error.message,
              code: error.code ?? 1
            });
          } else {
            resolve({
              stdout: decoder.decode(stdout).trim(),
              stderr: decoder.decode(stderr).trim(),
              code: 0
            });
          }
        }
      );

      this.activeProcesses.add(child);
      const timer = this.setupTimeout(child, timeout, reject);

      // 支持外部取消
      signal?.addEventListener('abort', () => {
        this.terminateProcess(child);
        reject(new Error('Command was aborted'));
      });

      child.on('exit', () => this.cleanupProcess(child));
    });
  }

  /**
   * 流式执行命令（新增方法）
   */
  public async execStream(
    command: string,
    options: ShellExecStreamOptions = {}
  ): Promise<boolean> {
    this.validateCommand(command);

    const { timeout = 10000, cwd, env, signal, onStdout, onStderr } = options;

    log.info("流式执行命令：", command);

    return new Promise((resolve, reject) => {
      if (signal?.aborted) {
        return reject(new Error('Command was aborted'));
      }

      const child = exec(
        command,
        {
          encoding: 'buffer',
          cwd: cwd,
          env: { ...process.env, ...env },
          timeout: timeout - 100
        },
        (error, stdout, stderr) => {
          this.cleanupProcess(child);
          clearTimeout(timer);
          if (error) {
            reject(error.message);
          } else {
            resolve(true);
          }
        }
      );

      this.activeProcesses.add(child);
      const timer = this.setupTimeout(child, timeout, reject);

      // 处理流式输出
      if (onStdout) {
        child.stdout?.on('data', (data: Buffer) => {
          onStdout(data.toString('utf-8'));
        });
      }

      if (onStderr) {
        child.stderr?.on('data', (data: Buffer) => {
          onStderr(data.toString('utf-8'));
        });
        // 添加这行来监听进程创建错误
        child.on('error', (err) => {
          onStderr("子进程创建失败");
        });
      }

      // 支持外部取消
      signal?.addEventListener('abort', () => {
        this.terminateProcess(child);
        reject(new Error('Command was aborted'));
      });

      child.on('exit', () => this.cleanupProcess(child));
    });
  }

  /**
   * 批量执行命令（支持串行/并行）
   */
  public async batchExec(
    commands: string[],
    options: BatchExecOptions = {}
  ): Promise<ShellResult[]> {
    if (options.parallel) {
      return Promise.all(
        commands.map(cmd =>
          this.exec(cmd).catch(e => e)
        )
      );
    }

    const results: ShellResult[] = [];
    for (const cmd of commands) {
      try {
        results.push(await this.exec(cmd));
      } catch (err) {
        if (options.stopOnError) throw err;
        results.push(err as ShellResult);
      }
    }
    return results;
  }

  /**
   * 清理所有正在运行的进程
   */
  public cleanupAll() {
    this.activeProcesses.forEach(child => {
      this.terminateProcess(child);
    });
  }

  // ================ 私有方法 ================
  private validateCommand(command: string) {
    if (typeof command !== 'string') {
      throw new Error('Command must be a string');
    }

    // 基础命令注入防护
    const dangerousChars = /[;&|<>`$\{\}\[\]\(\)]/;
    if (dangerousChars.test(command)) {
      throw new Error(`Command contains dangerous characters: ${command}`);
    }
  }

  private setupTimeout(
    child: ChildProcess,
    timeout: number,
    reject: (reason: any) => void
  ) {
    return setTimeout(() => {
      this.terminateProcess(child);
      reject({
        stdout: '',
        stderr: `Command timed out after ${timeout}ms`,
        code: 124 // UNIX timeout exit code
      });
    }, timeout);
  }

  private terminateProcess(child: ChildProcess) {
    try {
      child.kill('SIGTERM');
    } finally {
      this.cleanupProcess(child);
    }
  }

  private cleanupProcess(child: ChildProcess) {
    this.activeProcesses.delete(child);
  }
}

// ==================== IPC 安全暴露 ====================
export function registerShellIPC(service: ShellService) {
  ipcMain.handle('shell:exec', (_, command: string, options?: ShellExecOptions) =>
    service.exec(command, options)
  );

  ipcMain.handle('shell:execStream', (_, command: string, options?: ShellExecStreamOptions) =>
    service.execStream(command, options)
  );

  ipcMain.handle('shell:batchExec', (_, commands: string[], options?: BatchExecOptions) =>
    service.batchExec(commands, options)
  );

  ipcMain.handle('shell:cleanup', () => service.cleanupAll());

  // 进程退出时自动清理
  ipcMain.on('app:quit', () => service.cleanupAll());
}

// ==================== 单例导出 ====================
export const shellService = new ShellService();
